#include <iostream>
#include <vector>
#include <semaphore.h>
#include <pthread.h>
#include <cstdlib>
#include <unistd.h>

#define MAX 32

using namespace std;

template<class T>
class RingQueue
{
  private:
    vector<T> Queue;
    int cap;
    int push_pos=0;
    int pop_pos=0;
    sem_t blank_sem;
    sem_t data_sem;
  public:
    RingQueue(int _cap=MAX):cap(_cap)
    {
      sem_init(&blank_sem,0,cap);
      sem_init(&data_sem,0,0);
      Queue.resize(cap);
    }
    ~RingQueue()
    {
      sem_destroy(&blank_sem);
      sem_destroy(&data_sem);
    }
    void push(const T& in)
    {
        sem_wait(&blank_sem);
        Queue[push_pos]=in;
        push_pos++;
        push_pos%=cap;
        sem_post(&data_sem);
    }
    void pop(T& out)
    {
        sem_wait(&data_sem);
        out=Queue[pop_pos];
        pop_pos++;
        pop_pos%=cap;
        sem_post(&blank_sem);
    }

};

template<class T>
class Ring
{
  private:
    int a;
    int b;
    char c;
  public:

    void push(int& _a,int& _b,char& _c)
    { 
        a=_a;
        b=_b;
        c=_c;
    }
    void Run()
    {
      int z = 0;
            switch(c){
                case '+':
                    z = a + b;
                    break;
                case '-':
                    z = a - b;
                    break;
                case '*':
                    z = a * b;
                    break;
                case '/':
                    if(b == 0) std::cerr << "div zero!" << std::endl;
                    if(b != 0) z = a / b;
                    break;
                case '%':
                    if(b == 0) std::cerr << "mod zero!" << std::endl;
                    if(b != 0) z = a % b;
                    break;
                default:
                    perror("operator error!\n");
                    break;
            }
            std::cout << "thread: [" << pthread_self() << " ]: " << a << c << b << "=" << z << std::endl;
    }
    
};
void* push(void* gad)
{
    RingQueue<Ring<int>>* q=(RingQueue<Ring<int>>*)gad;
    while(true){
    int a=rand()%100+1;
    int b=rand()%100+1;
     char arr[]="+-*/%";
    Ring<int> abc;
    abc.push(a,b,arr[a%5]);
    q->push(abc);
    }   
} 
void* pop(void* agd)
{
  Ring<int> a;
  RingQueue<Ring<int>>* q=(RingQueue<Ring<int>>*)agd;
  while(true)
  {
    sleep(1);
    q->pop(a);
    a.Run();
  }
}
int main()
{
    RingQueue<Ring<int>> queue(30);
    srand((unsigned long)time(nullptr));
    pthread_t se1,se2,se3,se4;
    pthread_create(&se1,nullptr,push,&queue);
    pthread_create(&se2,nullptr,push,&queue);
    pthread_create(&se3,nullptr,pop,&queue);
    pthread_create(&se4,nullptr,pop,&queue);
  
    pthread_join(se1,nullptr);
    pthread_join(se2,nullptr);
    pthread_join(se3,nullptr);
    pthread_join(se4,nullptr);
  return 0;
}
